package com.gitee.wsl.transform.validator

import com.gitee.wsl.ext.any.mapTo
import com.gitee.wsl.ext.base.ifValue
import com.gitee.wsl.ext.base.isFalse
import com.gitee.wsl.func.InvokeCall
import com.gitee.wsl.struct.combine.BModifier
import kotlin.reflect.KClass

interface Validator<T,V:Validator<T,V>>: BModifier.Element<V>, InvokeCall<T, Boolean> {

    fun message(para:T): ValidationError = ValidationError.Generic(para.toString())

    fun validate(para:T):Boolean

    override operator fun invoke(t:T) = validate(t)

    //infix fun then(other: V): BModifier<V>
    //=if (other === Validator) this else BCombinedTModifier(this, other)

    companion object{
        val validatorsRegistry = hashMapOf<KClass<*>, Validator<*,*>>()

        inline fun <reified TClass, TValidator :Validator<TClass,TValidator>> registerValidator(validator: TValidator) {
            validatorsRegistry[TClass::class] = validator
        }

        inline fun <reified T:Any> validate(para: T): Boolean {
            val validator:Validator<T,*>?  =  validatorsRegistry[para::class] as Validator<T,*>?
            return validator?.validate(para)?:true
        }

        inline fun <reified T:Any> validateWithResult(para: T): Result<Boolean> {
            val validator = validatorsRegistry[para::class] as Validator<T,*>?
            return validator?.validateWithResult(para)?:Result.success(true)
        }
    }
}

interface ValidatorsRegistryScope {
    val validatorsRegistry: HashMap<KClass<*>, Validator<*, *>>
}

inline fun <reified TClass, TValidator :Validator<TClass,TValidator>> ValidatorsRegistryScope.registerValidator(validator: TValidator) {
    validatorsRegistry[TClass::class] = validator
}

class ValidatorsRegistry(
    override val validatorsRegistry: HashMap<KClass<*>, Validator<*, *>> =  HashMap()
):ValidatorsRegistryScope,Map<KClass<*>, Validator<*, *>> by validatorsRegistry{

    init {
        validatorsRegistry.putAll(Validator.validatorsRegistry)
    }

    inline fun <reified T:Any> validate(para: T): Boolean {
        val validator:Validator<T,*>?  =  Validator.validatorsRegistry[para::class] as Validator<T,*>?
        return validator?.validate(para)?:true
    }

    inline fun <reified T:Any> validateWithResult(para: T): Result<Boolean> {
        val validator = Validator.validatorsRegistry[para::class] as Validator<T,*>?
        return validator?.validateWithResult(para)?:Result.success(true)
    }
}

fun <T,V:Validator<T,V>> T.validateUse(rules: Validator<T,V>) = rules.foldIn(true){ flag, v->
    if(v.validate(this)) true else return@foldIn false
}

fun <T,V:Validator<T,V>> Validator<T,V>.validateWithResult(value:T)
= foldIn(mutableListOf<ValidationError>()){list,v->
    v.validate(value).isFalse { list.add(v.message(value)) }
    list
}.mapTo { it.isEmpty().ifValue(Result.success(true),Result.failure(ValidationException(it))) }

//fun <T,V:Validator<T,V>> T.validateWithMessage(validator: V)
//        = validator.foldIn(mutableListOf<ValidationError>()){list,v->
//             v.validate(this).isFalse { list.add(v.message(this)) }
//             list
//          }.mapTo { it.isEmpty().ifValue(Result.success(true),Result.failure(ValidationException(it))) }


